/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package principal;

import java.util.ArrayList;
import sun.net.idn.Punycode;

/**
 *
 * @author PabloGuillermo
 */
public class ConstelacionesLogica {
    
    public static final int BLANCA = 0;                                         //Constante de clase para las constelaciones blancas
    public static final int OSCURA = 1;                                         //Constante de clase para las constelaciones oscuras
    ConstelacionesEstado estado;
    
    public ConstelacionesLogica(){
        estado = new ConstelacionesEstado();
    }
    public ConstelacionesLogica(ConstelacionesEstado estado){
        this.estado=estado;
    }
    
    //Método que dada una coordenada (x,y) devuelve una estrella, la cual está próxima a esta coordenada
    //en un radio de 20px, en el formato (x,y,estado), donde estado tiene valor cero por defecto, ya que 
    //utiliza un vector de 3 enteros al igual que el método perteneceConstelación ( en este último
    //valor sí tiene un significado), pero para este método es irrelevante.
    //Si no encuentra estrella en ese rango, devolverá (-1,-1,-1)
    
    Integer[] esEstrella(int x, int y) {
        Integer [] resultado = new Integer[3];
        resultado[0]=-1; 
        resultado[1]=-1;
        resultado[2]=-1;
        //Evaluamos si alguna estrella (nodo) contiene a la coordenada ingresada en un radio de 20px
        for(Nodo n:estado.nodos){
            if(n.perteneceAConstelacion) continue;            
                
            if (distancia(x, y,n.x , n.y) < 20) {
                //n.perteneceAConstelacion=true;
                resultado[0]=n.x;
                resultado[1]=n.y;
                resultado[2]=0;
                break;
            }
        }
        
        return resultado;
    }
    
    //Distancia euclideana
    public double distancia(int x1, int y1, int x2, int y2) { 
        return Math.sqrt(Math.pow((x1 - x2), 2) + Math.pow(y1 - y2, 2));
    }  
    
    //Método para colocar un segmento en el juego
    //pos = 0, inserta al inicio
    //pos = 1, inserta al final
    public boolean insertarArista(int x1,int y1, int x2, int y2, int color, int pos, ArrayList<Integer> est){ 
        
        Arista a = new Arista(x1, y1, x2, y2);
        
        //CASO INICIAL - Primera jugada de Blancas y Oscuras
        if(contar(estado.segmentos)<2){ 
            int contNodos=0;                                                    //Contador de nodos comprendidos en la arista
            for(Nodo n: estado.nodos){
                //Si el nodo pertenece a la arista, aumentará el contador 
                if(pertenece(n,a)){
                    //n.perteneceAConstelacion=true;
                    contNodos++;
                }
            }
            System.out.println("Contador de nodos " + contNodos);
            if(contNodos>=2){ //Como regla inicial, una arista mínimamente debe cruzar dos nodos 
                Arista b=new Arista(x1,y1,x1+(int)(75 * ((x2-x1)/distancia(x1,y1,x2,y2)) ),y1+(int)(75 * ((y2-y1)/distancia(x1,y1,x2,y2)) ));
                //Como solo teníamos el punto inicial seleccionado y un punto final seleccionado, este punto inicial es correcto, ya que
                //contiene una estrella en su inicio, pero el punto final es la otra estrella seleccionada, y no necesariamente la distancia entre
                //estos puntos es 75(longitud de un segmento en el juego), es por esto que forzamos a que el segmento mida 75px, como tenemos dos puntos
                //y necesitamos un segmento más largo pero no debemos perder la dirección (debe cruzar el punto), para ello usamos la fórmula 
                //x1+75*cos(angulo_entre_la_arista_y_el_eje_x) para la primera coordenada y y1+75*sen(angulo_entre_la_arista_y_el_eje_x) 
                //con lo cual obtenemos un segmento de 75 px con la misma dirección del segmento señalado entre punto y punto
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(estado.segmentos[i], b)) return false;
                }
                b.estrellasContenidas=est;
                estado.segmentos[contar(estado.segmentos)]=b; //Insertamos el segmento
                
                return true;
            }
            return false;
        }
        
        //CASO MENOR QUE 5 - jugada n-ésima de Blancas y Oscuras tal que todavía no ha usado sus 5 segmentos cada uno
        if(contar(estado.segmentos)<10){
            Arista b=new Arista(x1,y1,x1+(int)(75 * ((x2-x1)/distancia(x1,y1,x2,y2)) ),y1+(int)(75 * ((y2-y1)/distancia(x1,y1,x2,y2)) ));
            b.estrellasContenidas=est;
            //Misma explicación que la anterior, pero en este caso ya está validado que el punto inicial pertenezca a una constelación
            for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(estado.segmentos[i], b)) return false;
            }
            //Si jugador hace inserción al extremo final de la constelación
            if(pos==1)
                estado.segmentos[contar(estado.segmentos)]=b;
            
            //Si jugador hace inserción al extremo inicial de la constelación
            else{
                moverElementosVector(contar(estado.segmentos)%2, 1);                   //corremos elementos del vector hacia la derecha
                estado.segmentos[(contar(estado.segmentos)-1)%2]=b;
            }
                
            return true;
        }
        //CASO IGUAL A 5 - jugada n-ésima de Blancas y Oscuras donde ya han usado sus 5 segmentos cada uno
        if(contar(estado.segmentos)==10){
            Arista b=new Arista(x1,y1,x1+(int)(75 * ((x2-x1)/distancia(x1,y1,x2,y2)) ),y1+(int)(75 * ((y2-y1)/distancia(x1,y1,x2,y2)) ));
            for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(estado.segmentos[i], b)) return false;
            }
            b.estrellasContenidas=est;
            int cAux;
            
            if(estado.turno == BLANCA){
                cAux = 2;
            }
            else{
                cAux = 1;
            }
            
            //Si jugador hace inserción al extremo final de la constelación
            if(pos==1){
               eliminarEstrellas(estado.segmentos[estado.turno]); 
               moverElementosVector(estado.turno, 0);                   //corremos elementos del vector hacia la izq
               estado.segmentos[contar(estado.segmentos)-cAux]=b;
            }
            
            //Si jugador hace inserción al extremo inicial de la constelación
            else{
                eliminarEstrellas(estado.segmentos[contar(estado.segmentos)-cAux]);
                moverElementosVector(estado.turno, 1);                   //corremos elementos del vector hacia la derecha
                estado.segmentos[estado.turno]=b;
            }
            
            return true;
            
        }
        
        
        return true;
    }
    
     //Método que devuelve verdad si un nodo (estrella) pertenece a una arista(segmento)
    private boolean pertenece(Nodo n, Arista a){
        //4 primeros casos cuando el punto está al inicio o al final de la arista
        if(a.x1==n.x){
            if(a.y1==n.y) return true;
            return false;
        }
        if(a.x2==n.x){
            if(a.y2==n.y) return true;
            return false;
        }
        if(a.y1==n.y){
            if(a.x1==n.x) return true;
            return false;
        }
        if(a.y2==n.y){
            if(a.x2==n.x) return true;
            return false;
        }
        
        //Si el punto está fuera de los límites del segmento (finito, a diferencia de una recta, devuelve inmediatamente falso)
        if(n.x>Math.max(a.x1,a.x2)) return false;
        if(n.y>Math.max(a.y1,a.y2)) return false;
        if(n.x<Math.min(a.x1,a.x2)) return false;
        if(n.y<Math.min(a.y1,a.y2)) return false;
        
        //Fórmula genérica que evalúa si un punto pertenece a una recta, como ya aplicamos previamente las validaciones para un 
        //segmento, podemos utilizarla para cuando el punto pueda estar dentro del segmento
        if((a.x2-n.x)/(a.y2-n.y)==(a.y1-n.y)/(a.x1-n.x)) return true; 
        return false;
    }
    
    //Método que inserta nodos, validando que la distancia entre ellos sea mayor o igual a 40
    public boolean insertarNodo(int x, int y, int n) {
        for (Nodo nodo : estado.nodos) {
            if (distancia(x, y, nodo.x, nodo.y) < 40) { //Si la distancia es menor a 40, no inserta el nodo
                return false;
            }
        }
        estado.nodos.add(new Nodo(x, y, n, false));
        System.out.println(n + " " + x + " " + y);
        return true;
    }   
    
    //Método que devuelve verdad si un punto genérico pertenece al inicio o final de la constelación, caso contrario es falso
    public Integer [] perteneceConstelacion(int x, int y){
        Integer [] resultado = new Integer[3];
        resultado[0]=-1;
        resultado[1]=-1;
        resultado[2]=-1;
        
        int cAux;
        if(contar(estado.segmentos)==10){
            if(estado.turno == BLANCA){
                cAux = 2;
            }
            else{
                cAux = 1;
            }
        }
        else{
            cAux = 2;
        }
        
        //Caso cuando el punto es final de constelacion
        int x1=estado.segmentos[contar(estado.segmentos)-cAux].x2;
        int y1=estado.segmentos[contar(estado.segmentos)-cAux].y2;
        if (distancia(x, y, x1 , y1) < 20) {
                System.out.println("final");
                resultado[0]=x1;
                resultado[1]=y1;
                resultado[2]=1; //final        
        }
        
        //Caso cuando el punto es inicio de constelacion
        
        x1=estado.segmentos[estado.turno].x1;
        y1=estado.segmentos[estado.turno].y1;
        if (distancia(x, y, x1 , y1) < 20) {
            System.out.println("inicial");    
            resultado[0]=x1;
            resultado[1]=y1;
            resultado[2]=0; //inicial        
        }
        
        System.out.println("resultado: " + resultado[0] + " " + resultado[1] + " " +resultado[2]); 
         
        return resultado;
    }
    
    //Método auxiliar para contar elementos de un vector, ya que al ser estático, java siempre da como resultado el tamaño del vector
    public int contar(Object [] vector){
        int contador =0;
        for(Object o: vector){
            if(o == null) continue;
            contador++;
        }
        
        return contador;
        
    }
    
    public void limpiarSegmentos(){
        estado.segmentos = new Arista[10];
    }
    
    //lado = 1: a la der
    //lado = 0: a la izq
    public void moverElementosVector(int color, int lado){
        System.out.println(color==0?"Blanca":"oscura");
        int tamanoVector = contar(estado.segmentos);
                
        int cder, cizq;
        
        if(tamanoVector==10){
            cder = 2;
        }
        else {
            cder = color==BLANCA?0:1;
        }
         
        cizq = 0;
        
        
        if (lado == 1){ 
            if(color == BLANCA){
                for (int i = 0; i < (tamanoVector/2) ; i++) {
                    if( tamanoVector-cder - 2< 0) continue;
                    estado.segmentos[tamanoVector-cder] = estado.segmentos[tamanoVector-(cder+2)];
                    cder+= 2;
                }
            }
            else {
                for (int i = 0; i < (tamanoVector/2) ; i++) {
                    if( tamanoVector-cder - 2 < 0) continue;
                    estado.segmentos[tamanoVector-cder+1] = estado.segmentos[tamanoVector-(cder+2)+1];
                    cder+= 2;
                }
            }
        }
        else {
            if(color == BLANCA){
                for (int i = 0; i < (tamanoVector/2) && (cizq+2)<10 ; i++) {
                    estado.segmentos[cizq] = estado.segmentos[cizq+2];
                    cizq+= 2;
                }
            }
            else {
                for (int i = 0; i < (tamanoVector/2) && (cizq+2)<10 ; i++) {
                    estado.segmentos[cizq+1] = estado.segmentos[cizq+3];
                    cizq+= 2;
                }
            }
        }          
    }
    
    public void eliminarEstrellas(Arista a){
        ArrayList<Nodo> eliminados = new ArrayList<>();
        for(Nodo n: estado.nodos){
            for(int i=0; i<a.estrellasContenidas.size()/2; i++){
                if(n.x == a.estrellasContenidas.get(i) && n.y == a.estrellasContenidas.get(i+1)){
                    eliminados.add(n);
                }
            }
        }
        for(Nodo n:eliminados){
            estado.nodos.remove(n);
        }
    }
    /*public void marcarObjetivo(int x, int y, int turno){
        int tam=contar(estado.segmentos);
        boolean inicioValido=true;
        boolean finalValido=true;
        if(tam<2) return;
        int di=999;//Distancia desde el inicio de la constelación
        int df=999;//Distancia desde el fin de la constelación
        if(turno==BLANCA){
            for(int i=0; i<contar(estado.segmentos); i++){
                if(intersectanAristas(new Arista(x, y, estado.segmentos[0].x1,estado.segmentos[0].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                    inicioValido=false; break;                   
                }
            }
            if(inicioValido){
                 di=(int)distancia(estado.segmentos[0].x1,estado.segmentos[0].y1,x,y);
            }
            if(tam<10){
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(new Arista(x, y, estado.segmentos[tam-(2-tam%2)].x1,estado.segmentos[tam-(2-tam%2)].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                        finalValido=false; break;                   
                    }
                }
                if(finalValido){
                    df=(int)distancia(estado.segmentos[tam-(2-tam%2)].x1,estado.segmentos[tam-(2-tam%2)].y1,x,y);
                }
                if(di<df){
                    estado.puntoCercanoObjB[0]=estado.segmentos[0].x1;
                    estado.puntoCercanoObjB[1]=estado.segmentos[0].y1;
                }
                else{
                    estado.puntoCercanoObjB[0]=estado.segmentos[tam-(2-tam%2)].x1;
                    estado.puntoCercanoObjB[1]=estado.segmentos[tam-(2-tam%2)].y1;
                }
            }
            else{
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(new Arista(x, y, estado.segmentos[8].x1,estado.segmentos[8].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                        finalValido=false; break;                   
                    }
                }
                if(finalValido){
                    df=(int)distancia(estado.segmentos[8].x1,estado.segmentos[8].y1,x,y);
                }
                if(di<df){
                    estado.puntoCercanoObjB[0]=estado.segmentos[0].x1;
                    estado.puntoCercanoObjB[1]=estado.segmentos[0].y1;
                }
                else{
                    estado.puntoCercanoObjB[0]=estado.segmentos[8].x1;
                    estado.puntoCercanoObjB[1]=estado.segmentos[8].y1;
                }
            }
            if(finalValido || inicioValido){
                estado.objetivoB[0]=x;
                estado.objetivoB[1]=y;
            }
        }
        else{
            for(int i=0; i<contar(estado.segmentos); i++){
                if(intersectanAristas(new Arista(x, y, estado.segmentos[1].x1,estado.segmentos[1].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                    inicioValido=false; break;                   
                }
            }
            if(inicioValido){
                 di=(int)distancia(estado.segmentos[1].x1,estado.segmentos[1].y1,x,y);
            }
            if(tam<10){
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(new Arista(x, y, estado.segmentos[tam-(1+tam%2)].x1,estado.segmentos[tam-(1+tam%2)].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                        finalValido=false; break;                   
                    }
                }
                if(finalValido){
                    df=(int)distancia(estado.segmentos[tam-(1+tam%2)].x1,estado.segmentos[tam-(1+tam%2)].y1,x,y);
                }
                if(di<df){
                    estado.puntoCercanoObjO[0]=estado.segmentos[0].x1;
                    estado.puntoCercanoObjO[1]=estado.segmentos[0].y1;
                }
                else{
                    estado.puntoCercanoObjO[0]=estado.segmentos[tam-(1+tam%2)].x1;
                    estado.puntoCercanoObjO[1]=estado.segmentos[tam-(1+tam%2)].y1;
                }
            }
            else{
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(new Arista(x, y, estado.segmentos[9].x1,estado.segmentos[9].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                        finalValido=false; break;                   
                    }
                }
                if(finalValido){
                    df=(int)distancia(estado.segmentos[9].x1,estado.segmentos[9].y1,x,y);
                }
                if(di<df){
                    estado.puntoCercanoObjO[0]=estado.segmentos[0].x1;
                    estado.puntoCercanoObjO[1]=estado.segmentos[0].y1;
                }
                else{
                    estado.puntoCercanoObjO[0]=estado.segmentos[9].x1;
                    estado.puntoCercanoObjO[1]=estado.segmentos[9].y1;
                }
            }
            if(finalValido || inicioValido){
                estado.objetivoO[0]=x;
                estado.objetivoO[1]=y;
            }            
        }
    }*/
    
    public void marcarObjetivo(int x1, int y1, int x2, int y2,int turno){
        int tam=contar(estado.segmentos);              
        if(tam<2) return;
        for(int i=0; i<contar(estado.segmentos); i++){
                if(intersectanAristas(new Arista(x1, y1, x2,y2), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                    return;                   
            }
        }    
        
        if(turno==BLANCA){
            estado.puntoCercanoObjB[0]=x1;
            estado.puntoCercanoObjB[1]=y1;
            estado.objetivoB[0]=x2;
            estado.objetivoB[1]=y2;
        }
        else{
            estado.puntoCercanoObjO[0]=x1;
            estado.puntoCercanoObjO[1]=y1;
            estado.objetivoO[0]=x2;
            estado.objetivoO[1]=y2;
        }
                
    }
    
    public int productoCruz(int ax, int ay, int bx, int by,int cx, int cy){
        return (bx-ax)*(cy-ay) - (by-ay)*(cx-ax);
    }
    
    public boolean intersectanAristas(Arista a, Arista b){
        int d1,d2,d3,d4;
        d1 = productoCruz(b.x1,b.y1,b.x2,b.y2,a.x1,a.y1);
        d2 = productoCruz(b.x1,b.y1,b.x2,b.y2,a.x2,a.y2);
        d3 = productoCruz(a.x1,a.y1,a.x2,a.y2,b.x1,b.y1);
        d4 = productoCruz(a.x1,a.y1,a.x2,a.y2,b.x2,b.y2);
        if(((d1>0 && d2<0) || (d1<0 && d2>0)) &&
            ((d3>0 && d4<0) || (d3<0 && d4>0)))
            return true;

        /*if(d1==0 && pertenece(new Nodo(a.x1,a.y1,0,false),b))
            return true;
        if(d2==0 && pertenece(new Nodo(a.x2,a.y2,0,false),b))
            return true;    
        if(d3==0 && pertenece(new Nodo(b.x1,b.y1,0,false),a))
            return true;
        if(d4==0 && pertenece(new Nodo(b.x2,b.y2,0,false),a))
            return true;*/

        return false;
    }
    
    public int finJuego() {
        //System.out.println("Turno : "+estado.turno);
        int tam = contar(estado.segmentos);
        if(tam<=2) return -1;  
       
        
        for(Nodo n:estado.nodos){
            if(n.perteneceAConstelacion) continue;
            if(existeObjetivo(n.x, n.y, estado.turno)) return -1;
        }
        
        return estado.turno==OSCURA?1:0;     
                
    }
    
    public boolean existeObjetivo(int x, int y, int turno){
        int tam=contar(estado.segmentos);
        boolean inicioValido=true;
        boolean finalValido=true;
        if(tam<2) return false;
        
        if(turno==BLANCA){
            for(int i=0; i<contar(estado.segmentos); i++){
                if(intersectanAristas(new Arista(x, y, estado.segmentos[0].x1,estado.segmentos[0].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                    inicioValido=false; break;                   
                }
            }            
            if(tam<10){
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(new Arista(x, y, estado.segmentos[tam-(2-tam%2)].x1,estado.segmentos[tam-(2-tam%2)].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                        finalValido=false; break;                   
                    }
                }
            }
            else{
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(new Arista(x, y, estado.segmentos[8].x1,estado.segmentos[8].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                        finalValido=false; break;                   
                    }
                }
            }
            if(finalValido || inicioValido){
                return true;
            }
        }
        else{
            for(int i=0; i<contar(estado.segmentos); i++){
                if(intersectanAristas(new Arista(x, y, estado.segmentos[1].x1,estado.segmentos[1].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                    inicioValido=false; break;                   
                }
            }            
            if(tam<10){
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(new Arista(x, y, estado.segmentos[tam-(1+tam%2)].x1,estado.segmentos[tam-(1+tam%2)].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                        finalValido=false; break;                   
                    }
                }
            }
            else{
                for(int i=0; i<contar(estado.segmentos); i++){
                    if(intersectanAristas(new Arista(x, y, estado.segmentos[9].x1,estado.segmentos[9].y1), estado.segmentos[i])){ //Donde new Arista es A sub 0 de nuestras reglas
                        finalValido=false; break;                   
                    }
                }                
            }
            if(finalValido || inicioValido){
                return true;
            }
            if(finalValido || inicioValido){
                return true;
            }
        }
        return false;
    }
}
